#ifndef _DX11INITIALISER_H
#define _DX11INITIALISER_H
#include "Optional.h"
#include "Window.h"
#include "DX11Structure.h"
#include <dxgi.h>
#include <d3d11.h>
#include <vector>

class DX11Initialiser {

private:
	Window * window;

	Optional<int> vsyncFrequency;
	Optional<DXGI_FORMAT> dxgiFormat;
	Optional<DXGI_MODE_SCANLINE_ORDER> scanlineOrdering;
	Optional<DXGI_MODE_SCALING> scaling;
	Optional<DXGI_SAMPLE_DESC> sampling;
	Optional<DXGI_USAGE> dxgiUsage;
	Optional<int> bufferCount;
	Optional<DXGI_SWAP_EFFECT> swapEffect;
	Optional<D3D_DRIVER_TYPE> driverType;
	Optional<HMODULE> softwareDriverHandle;
	bool isDebug = false;
	bool isSingleThreaded = false;
	bool supportsBGRAFormats = false;
	Optional<pair<int, int>> depthStencilSize;
	Optional<int> depthStencilMipmapLevels;
	Optional<DXGI_FORMAT> depthStencilFormat;
	Optional<DXGI_SAMPLE_DESC> depthStencilSampling;
	Optional<D3D11_USAGE> depthStencilUsage;
	Optional<int> depthStencilCPUAccessFlags;
	Optional<int> depthStencilMiscFlags;

	static const int DEFAULT_VSYNC_FREQUENCY = 0;
	static const DXGI_FORMAT DEFAULT_DXGI_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM;
	static const DXGI_MODE_SCANLINE_ORDER DEFAULT_SCANLINE_ORDERING	= DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
	static const DXGI_MODE_SCALING DEFAULT_SCALING = DXGI_MODE_SCALING_STRETCHED;
	static const int DEFAULT_SAMPLING_COUNT = 1;
	static const int DEFAULT_SAMPLING_QUALITY = 0;
	static const DXGI_USAGE	DEFAULT_DXGI_USAGE = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	static const int DEFAULT_BUFFER_COUNT = 1;
	static const DXGI_SWAP_EFFECT DEFAULT_SWAP_EFFECT = DXGI_SWAP_EFFECT_DISCARD;
	static const D3D_DRIVER_TYPE DEFAULT_DRIVER_TYPE = D3D_DRIVER_TYPE_UNKNOWN;
	static const UINT DEFAULT_DEVICE_FLAGS = D3D11_CREATE_DEVICE_DEBUG;
	static const int DEFAULT_DEPTH_STENCIL_WIDTH = TurnsDataModel::WINDOW_DEFAULT_WIDTH;
	static const int DEFAULT_DEPTH_STENCIL_HEIGHT = TurnsDataModel::WINDOW_DEFAULT_HEIGHT;
	static const int DEFAULT_DEPTH_STENCIL_MIPMAP_LEVELS = 1;
	static const DXGI_FORMAT DEFAULT_DEPTH_STENCIL_FORMAT = DXGI_FORMAT_D24_UNORM_S8_UINT;
	static const int DEFAULT_DEPTH_STENCIL_SAMPLING_COUNT = 1;
	static const int DEFAULT_DEPTH_STENCIL_SAMPLING_QUALITY = 0;
	static const D3D11_USAGE DEFAULT_DEPTH_STENCIL_USAGE = D3D11_USAGE_DEFAULT;
	static const int DEFAULT_DEPTH_STENCIL_CPU_ACCESS_FLAGS = 0;
	static const int DEFAULT_DEPTH_STENCIL_MISC_FLAGS = 0;

public:
	DX11Initialiser(Window * pWindow) { window = pWindow; }
	~DX11Initialiser();

	Optional<DX11Structure> initDX11();

	DX11Initialiser * setVSync(int frequency);
	DX11Initialiser * setDXGIFormat(DXGI_FORMAT pDXGIFormat);
	DX11Initialiser * setScanlineOrdering(DXGI_MODE_SCANLINE_ORDER pScanlineOrdering);
	DX11Initialiser * setScaling(DXGI_MODE_SCALING pScaling);
	DX11Initialiser * setSampling(int count, int quality);
	DX11Initialiser * setDXGIUsage(DXGI_USAGE pDXGIUsage);
	DX11Initialiser * setBufferCount(int pBufferCount);
	DX11Initialiser * setSwapEffect(DXGI_SWAP_EFFECT pSwapEffect);
	DX11Initialiser * setDriverType(D3D_DRIVER_TYPE pDriverType, Optional<HMODULE> softwareDriverHandle);
	DX11Initialiser * setDebugMode(bool pIsDebug);
	DX11Initialiser * makeSingleThreaded();
	DX11Initialiser * makeSupportBGRAFormats();
	DX11Initialiser * setDepthStencilSize(int pDepthStencilWidth, int pDepthStencilHeight);
	DX11Initialiser * setDepthStencilMipmapLevels(int pDepthStencilMipmapLevels);
	DX11Initialiser * setDepthStencilFormat(DXGI_FORMAT pDepthStencilFormat);
	DX11Initialiser * setDepthStencilSampling(int count, int quality);
	DX11Initialiser * setDepthStencilUsage(D3D11_USAGE pDepthStencilUsage);
	DX11Initialiser * setDepthStencilCPUAccessFlags(int pDepthStencilCPUAccessFlags);
	DX11Initialiser * setDepthStencilMiscFlags(int pDepthStencilMiscFlags);

private:
	DXGI_MODE_DESC createBufferDescription();
	DXGI_SWAP_CHAIN_DESC createSwapChainDescription(DXGI_MODE_DESC pBufferDescription, Window *pWindow);
	vector<IDXGIAdapter*> getVideoCards();
	UINT createDeviceFlags();
	bool tryCreateDeviceAndSwapChain(DX11Structure *pStructure, DXGI_SWAP_CHAIN_DESC *pSwapChainDescription);
	HRESULT createDeviceAndSwapChain(IDXGIAdapter *pVideoCard, UINT pDeviceFlags, DXGI_SWAP_CHAIN_DESC *pSwapChainDescription,
		IDXGISwapChain **ppSwapChain, ID3D11Device **ppDevice, ID3D11DeviceContext **ppDeviceContext);
	bool tryGetBackBuffer(DX11Structure *pStructure);
	bool tryCreateRenderTargetView(DX11Structure *pStructure);
	D3D11_TEXTURE2D_DESC createDepthStencilDescription();
	void createDepthStencilView(DX11Structure *pStructure, D3D11_TEXTURE2D_DESC *pDepthStencilDescription);
	void bindViewsToOutputMergerStage(DX11Structure *pStructure);
	
};

#endif
